﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;
using Konst3d.Efficiency.Suite;

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class MilestoneStorage : IMilestoneCollection, IMilestoneStorage, IDisposable
    {
        #region Constants

        private const string MILESTONE_FILE_EXT = ".milestones";

        #endregion // Constants

        #region Create/Open/Delete Methods

        public static MilestoneStorage Create(string fileName, bool overwrite, ICryptographicEngine cryptographicEngine) // file name w/o ext
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            MilestoneFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = MilestoneFile.Create(fileName + MILESTONE_FILE_EXT, overwrite);
                labels = LabelsStorage.Create(fileName, overwrite, cryptographicEngine);

                return new MilestoneStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public static MilestoneStorage Open(string fileName, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            MilestoneFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = MilestoneFile.Open(fileName + MILESTONE_FILE_EXT);
                labels = LabelsStorage.Open(fileName, cryptographicEngine);

                return new MilestoneStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public void Delete()
        {
            Contract.Assert(!_disposed);

            _file.Delete();
            _labels.Delete();

            Dispose();
        }

        #endregion // Create/Open/Delete Methods

        #region Constructor/Finalizer

        private MilestoneStorage(MilestoneFile file, LabelsStorage labels)
        {
            Contract.Requires(file != null);
            Contract.Requires(labels != null);

            _file = file;
            _labels = labels;

            var entries = _file.GetAll();

            foreach (var entry in entries)
            {
                var milestone = new Milestone(this, entry.Key, entry.Value.Date, _labels.GetLabel(entry.Value.TitleID), _labels.GetLabel(entry.Value.DescriptionID));

                _milestones.Add(milestone.ID, milestone);

                if (_dates.ContainsKey(milestone.Date))
                {
                    _dates[milestone.Date].Add(milestone);
                }
                else
                {
                    _dates.Add(milestone.Date, new List<Milestone>());
                    _dates[milestone.Date].Add(milestone);
                }
            }
        }

        ~MilestoneStorage()
        {
            Dispose(false);
        }

        #endregion // Constructor/Finalizer

        #region IMilestoneCollectionStorage Members

        public IMilestone this[int id]
        {
            get
            {
                Contract.Assert(_milestones.ContainsKey(id));
                return _milestones[id];
            }
        }

        public int Add(DateTime date, string title, string description)
        {
            int titleID = _labels.AddLabel(title);
            int descrID = _labels.AddLabel(description);

            DateTime shortDate = new DateTime(date.Year, date.Month, date.Day); // DT w/o hours\minutes\secs...

            int id = _file.AddEntry(shortDate, titleID, descrID);
            var milestone = new Milestone(this, id, shortDate, title, description);

            _milestones.Add(id, milestone);

            if (_dates.ContainsKey(shortDate))
            {
                _dates[shortDate].Add(milestone);
            }
            else
            {
                _dates.Add(shortDate, new List<Milestone>());
                _dates[shortDate].Add(milestone);
            }

            return id;
        }

        public void Remove(int id)
        {
            var milestone = _milestones[id];

            _labels.DeleteLabel(_file.GetEntryTitleID(id));
            _labels.DeleteLabel(_file.GetEntryDescrID(id));

            _file.RemoveEntry(id);
            _milestones.Remove(id);

            if (_dates.ContainsKey(milestone.Date))
            {
                _dates[milestone.Date].Remove(milestone);
            }
        }

        public IEnumerable<IMilestone> GetMilestones(DateTime date)
        {
            DateTime shortDate = new DateTime(date.Year, date.Month, date.Day);

            if (!_dates.ContainsKey(shortDate))
            {
                _dates.Add(shortDate, new List<Milestone>());
            }

            return _dates[shortDate];
        }

        #endregion

        #region IEnumerable<IMilestone> Members

        public IEnumerator<IMilestone> GetEnumerator()
        {
            return _milestones.Values.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _milestones.Values.GetEnumerator();
        }

        #endregion

        #region IMilestoneStorage Members

        void IMilestoneStorage.UpdateDate(int id, DateTime date)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_milestones.ContainsKey(id));

            _file.SetEntryDate(id, date);
        }

        void IMilestoneStorage.UpdateTitle(int id, string title)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_milestones.ContainsKey(id));

            int labelID = _file.GetEntryTitleID(id);
            int newLabelID = _labels.UpdateLabel(labelID, title);

            _file.SetEntryTitleID(id, newLabelID);
        }

        void IMilestoneStorage.UpdateDescription(int id, string description)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_milestones.ContainsKey(id));

            int labelID = _file.GetEntryDescrID(id);
            int newLabelID = _labels.UpdateLabel(labelID, description);

            _file.SetEntryDescrID(id, newLabelID);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _file.Dispose();
                    _file = null;

                    _labels.Dispose();
                    _labels = null;

                    _milestones.Clear();
                    _milestones = null;

                    _dates.Clear();
                    _dates = null;
                }

                _disposed = true;
            }
        }

        #endregion

        #region Fields

        private bool _disposed = false;
        private MilestoneFile _file;
        private LabelsStorage _labels;
        private Dictionary<int, Milestone> _milestones = new Dictionary<int,Milestone>();
        private Dictionary<DateTime, List<Milestone>> _dates = new Dictionary<DateTime, List<Milestone>>();

        #endregion // Fields
    }
}
